home *** CD-ROM | disk | FTP | other *** search
- // BoinkViewPart.m
- //
- // implements a bouncing ball screen saver view
- //
- // You may freely copy, distribute, and reuse the code in this example.
- // NeXT disclaims any warranty of any kind, expressed or implied, as to its
- // fitness for any particular use.
-
-
- #import "BoinkViewPart.h"
- #import "SpaceView.h"
- #import "Thinker.h"
- #import "BoinkWraps.h"
- #import <appkit/NXImage.h>
- #import <math.h>
- #import <libc.h>
- #import <dpsclient/wraps.h>
-
- @implementation BoinkView
-
- // assumed interval in milliseconds
- #define ASSUMED_INTERVAL 35
-
- #define WIDTH 100
- #define HEIGHT 100
- #define GAP 4
- #define COUNT 10
- #define ACCEL (-2)
- #define REBOUND (-1.3)
-
- // This screen height value is not critical, though it will be used
- // to determine how high the ball can go
- #define SCREEN_HEIGHT 832
- #define LAUNCH_SPEED (sqrt(fabs(2*ACCEL*(SCREEN_HEIGHT - HEIGHT))))
- #define REAL_LAUNCH_SPEEd (sqrt(fabs(2*accel*(viewHeight - HEIGHT))))
-
- #define MIN_X_SPEED (3)
- #define MAX_X_SPEED (6)
- #define ABS_MAX_X_SPEED (6)
- #define MAX_Y_SPEED (LAUNCH_SPEED + 20)
-
- #define BUFFER_WIDTH (WIDTH + ABS_MAX_X_SPEED + 1)
- #define BUFFER_HEIGHT (HEIGHT + MAX_Y_SPEED + 1)
-
-
-
- /* move the ball to its new bounce position */
- - oneStep
- {
- NXRect black = {0,0,0,0};
- NXRect ballRect;
- BRECT new;
- float scaledTime, calcYpos;
-
- then = now;
- now = currentTimeInMs();
-
- /* calculate new ball x position */
- xpos += [self timeCorrectedXSpeed];
-
- if (xpos < 0) /* ball hit left edge */
- { xspeed = -xspeed;
- if (viewWidth > WIDTH)
- { spinDir = -spinDir;
- }
- xpos = 0;
- }
- else if (xpos > (viewWidth - WIDTH)) /* ball hit right edge */
- { if (viewWidth > WIDTH)
- {
- xspeed = -[self getRandomXspeed];
- [self checkXspeed:&xspeed];
- xpos = (viewWidth - WIDTH);
- }
- else
- { xspeed = xpos = 0;
- }
- }
-
-
- scaledTime = ((float)(now - then) / ASSUMED_INTERVAL);
- if (scaledTime > 1) scaledTime = 1;
-
- // calculate new ball vertical position
- calcYpos = ypos + (scaledTime*yspeed) + ((accel * scaledTime * scaledTime)/2);
-
- // change vertical ball speed to simulate gravity
- yspeed += (accel * scaledTime);
-
- if (calcYpos < (ypos - MAX_Y_SPEED)) calcYpos = ypos - MAX_Y_SPEED;
- else if (calcYpos > (ypos + MAX_Y_SPEED)) calcYpos = ypos + MAX_Y_SPEED;
-
- ypos = calcYpos;
-
- if (yspeed < -MAX_Y_SPEED) yspeed = -MAX_Y_SPEED;
-
-
- if (ypos <= 0) /* ball hit bottom of window */
- {
- ypos = 0;
-
- if (viewHeight > HEIGHT)
- {
- if (reboundMode == DECREASING)
- {
- yspeed = lastLaunchSpeed = lastLaunchSpeed + rebound;
- }
- else
- {
- yspeed = lastLaunchSpeed = lastLaunchSpeed - (2*rebound);
- }
-
- if (yspeed <= 0)
- {
- yspeed = 0;
- reboundMode = INCREASING; /* bounce height increases every bounce */
- }
- else if (yspeed > MAX_Y_SPEED) yspeed = MAX_Y_SPEED - (3*accel);
- }
- else yspeed = 0;
-
- }
- else if (ypos >= (viewHeight - HEIGHT)) /* ball hit top of window */
- { if (viewHeight > HEIGHT)
- {
- yspeed = accel;
- ypos = (viewHeight - HEIGHT);
- reboundMode = DECREASING; /* bounce height decreases every bounce */
- spinDir = -spinDir;
- }
- else
- { yspeed = ypos = 0;
- }
- }
-
-
- /* rotate the ball by selecting a new ball image to blit */
- /* we have an image of the ball in 10 different stages of rotation */
-
- [self incrementBallNumber];
-
- new.l = floor(xpos);
- new.b = floor(ypos);
- new.r = new.l + WIDTH;
- new.t = new.b + HEIGHT;
-
- ballRect.origin.x = (WIDTH+GAP) * ballNum;
- ballRect.origin.y = 0;
- ballRect.size.width = WIDTH;
- ballRect.size.height = HEIGHT;
-
- redrawTo.x = MIN(new.l, old.l);
- redrawTo.y = MIN(new.b, old.b);
-
- redraw.origin.x = 0;
- redraw.origin.y = 0;
- redraw.size.width = (MAX(new.r, old.r)) - redrawTo.x + 1;
- redraw.size.height = (MAX(new.t, old.t)) - redrawTo.y + 1;
-
- black.size= redraw.size;
-
- [self updateGrid];
-
- [buffer lockFocus];
- PSsetgray(0);
- NXRectFill(&black);
-
- ballTo.x = new.l - redrawTo.x;
- ballTo.y = new.b - redrawTo.y;
-
- [self drawLinesInBuffer];
-
- [balls composite:NX_SOVER fromRect:&ballRect toPoint:&ballTo];
- [buffer unlockFocus];
-
-
- // Now bring it onto the screen
-
- [buffer composite:NX_COPY fromRect:&redraw toPoint:&redrawTo];
-
- old = new;
-
- return self;
- }
-
-
-
- /* calculate a vertical launch speed which will get the ball almost to */
- /* the top of the window before gravity pulls it back down. Little bit */
- /* of physics lesson here... */
-
- - newSpeed
- {
- lastLaunchSpeed = yspeed = REAL_LAUNCH_SPEEd;
- if (yspeed > MAX_Y_SPEED) yspeed = MAX_Y_SPEED;
- xpos = 0;
- ypos = 0;
-
- if (viewWidth <= WIDTH) xspeed = 0;
- else xspeed = [self getRandomXspeed];
-
- [self checkXspeed:&xspeed];
- rebound = REBOUND;
- return self;
- }
-
-
- - initFrame:(const NXRect *)frameRect
- {
- NXRect black = {0, 0, BUFFER_WIDTH, BUFFER_HEIGHT };
-
- [super initFrame:frameRect];
- [self allocateGState]; // For faster lock/unlockFocus
- [self setClipping:NO]; // even faster...
-
- accel = ACCEL;
- spinDir = 1;
-
- //in this case, I only need one buffer for several Views
- if (!(buffer = [NXImage findImageNamed:"boinkBuffer"]))
- {
- buffer = [[NXImage alloc] initSize:&black.size];
- [buffer setName:"boinkBuffer"];
- }
-
- if ([buffer lockFocus])
- {
- PSsetgray(0);
- NXRectFill(&black);
- [buffer unlockFocus];
- }
-
- balls = [NXImage findImageNamed:"balls"];
-
- [self newViewSize];
-
- return self;
- }
-
- - setAccel:(float)val
- {
- accel = val;
- return self;
- }
-
- - sizeTo:(NXCoord)width :(NXCoord)height
- {
- [super sizeTo:width :height];
- [self newViewSize];
- return self;
- }
-
- - drawSelf:(const NXRect *)rects :(int)rectCount
- {
- if (!rects || !rectCount) return self;
-
- //PSsetgray(0);
- //NXRectFill(rects);
-
- NXRectClip(rects);
- [self drawGrid];
-
- return self;
- }
-
- - newViewSize
- {
- int i;
- //this is called every time View size changes
- NXRect black = {0, 0, BUFFER_WIDTH, BUFFER_HEIGHT };
-
- then = now = currentTimeInMs();
-
- if (oldSize.width == bounds.size.width &&
- oldSize.height == bounds.size.height)
- return self;
- else
- {
- oldSize.width = bounds.size.width;
- oldSize.height = bounds.size.height;
- }
-
- old.l = old.r = old.b = old.t = ballTo.x = ballTo.y = 0;
-
- viewWidth = bounds.size.width;
- viewHeight = bounds.size.height;
- if (viewHeight > SCREEN_HEIGHT) viewHeight = SCREEN_HEIGHT;
-
- nvert = viewWidth/130;
- if (nvert > NVERT) nvert = NVERT;
- nhoriz = viewHeight/130;
- if (nhoriz > NHORIZ) nhoriz = NHORIZ;
-
- if (viewWidth < WIDTH) nvert = 0;
- if (viewHeight < HEIGHT) nhoriz= 0;
- vcount = hcount = 0;
-
- for (i=0; i<nvert; i++)
- {
- vertLines[i].hue = i * 0.17;
- while (vertLines[i].hue > 1) vertLines[i].hue -= 1;
- vertLines[i].pos = floor(i * (viewWidth/nvert));
- }
-
- for (i=0; i<nhoriz; i++)
- {
- horizLines[i].hue = i * 0.17 + 0.1;
- while (horizLines[i].hue > 1) horizLines[i].hue -= 1;
- horizLines[i].pos = i * floor((viewHeight/nhoriz)) + 1;
- }
-
- if ([buffer lockFocus])
- {
- PSsetgray(0);
- NXRectFill(&black);
- [buffer unlockFocus];
- }
-
- [self newSpeed];
- return self;
- }
-
- - incrementBallNumber
- {
- if (now > nextRotationTime)
- {
- ballNum += spinDir;
-
- if (ballNum >= COUNT) ballNum = 0;
- else if (ballNum < 0) ballNum = COUNT-1;
- nextRotationTime = now + 24;
- }
-
- return self;
- }
-
- - (float) getRandomXspeed
- {
- return randBetween(MIN_X_SPEED, MAX_X_SPEED);
- }
-
- - (float) timeCorrectedXSpeed
- {
- float ret = xspeed * ((float)(now - then) / ASSUMED_INTERVAL);
- [self checkXspeed:&ret];
- return ret;
- }
-
- - checkXspeed:(float *)speed
- {
- if (*speed > MAX_X_SPEED) *speed = MAX_X_SPEED;
- else if (*speed < -MAX_X_SPEED) *speed = -MAX_X_SPEED;
- return self;
- }
-
- - (const char *)windowTitle
- {
- return "Boink!";
- }
-
-
- - drawGrid
- {
- int i;
- float *fp;
-
- for (i=0; i<nvert; i++)
- {
- fp = &vertLines[i].pos;
- colorLine(*fp, 0, *fp, viewHeight, vertLines[i].hue, 1);
- }
-
- for (i=0; i<nhoriz; i++)
- {
- fp = &horizLines[i].pos;
- colorLine(0, *fp, viewWidth, *fp, horizLines[i].hue, 1);
- }
-
- return self;
- }
-
- - updateGrid
- {
- NXRect avoid;
- float oldPos;
- float *fp;
-
- if (!nvert && !nhoriz) return self;
-
- if (now < nextLineDrawTime) return self;
-
- nextLineDrawTime = now + 3300;
-
- avoid.origin = redrawTo;
- avoid.size = redraw.size;
-
- if (++toggle & 1)
- {
- //advance vertical line
-
- if (!nvert) return self;
-
- fp = &vertLines[vcount].pos;
- oldPos = *fp;
- *fp += 1;
- if (*fp > viewWidth) *fp = 0;
- vertLines[vcount].hue += 0.005;
- if (vertLines[vcount].hue > 1) vertLines[vcount].hue -= 1;
-
-
- verticalLineWithAvoidance(*fp, 0, *fp, viewHeight, vertLines[vcount].hue, 1, &avoid);
- verticalLineWithAvoidance(oldPos, 0, oldPos, viewHeight, 0, 0, &avoid);
-
- if (++vcount >= nvert) vcount = 0;
- }
- else
- {
- //advance horiz line
-
- if (!nhoriz) return self;
-
- fp = &horizLines[hcount].pos;
- oldPos = *fp;
- *fp += 1;
- if (*fp > viewHeight) *fp = 0;
- horizLines[hcount].hue += 0.005;
- if (horizLines[hcount].hue > 1) horizLines[hcount].hue -= 1;
-
-
- horizLineWithAvoidance(0, *fp, viewWidth, *fp, horizLines[hcount].hue, 1, &avoid);
- horizLineWithAvoidance(0, oldPos, viewWidth, oldPos, 0, 0, &avoid);
-
- if (++hcount >= nhoriz) hcount = 0;
- }
-
- return self;
- }
-
- void horizLineWithAvoidance(float x1, float y1, float x2,float y2,
- float hue,float brightness, const NXRect *r)
- {
- if (y1 <= r->origin.y || y1 >= r->origin.y+r->size.height)
- colorLine(x1, y1, x2, y2, hue, brightness);
- else
- {
- colorLine(x1, y1, r->origin.x, y2, hue, brightness);
- colorLine(r->origin.x+r->size.width, y1, x2, y2, hue, brightness);
- }
- }
-
- void verticalLineWithAvoidance(float x1, float y1, float x2,float y2,
- float hue,float brightness, const NXRect *r)
- {
- if (x1 <= r->origin.x || x1 >= r->origin.x+r->size.width)
- colorLine(x1, y1, x2, y2, hue, brightness);
- else
- {
- colorLine(x1, y1, x2, r->origin.y, hue, brightness);
- colorLine(x1, r->origin.y+r->size.height, x2, y2, hue, brightness);
- }
- }
-
- - drawLinesInBuffer
- {
- NXRect avoid;
- int i;
-
- avoid.origin = redrawTo;
- avoid.size = redraw.size;
-
- for (i=0; i<nvert; i++)
- {
- float x = vertLines[i].pos;
-
- if (x >= avoid.origin.x && x <= avoid.origin.x + avoid.size.width)
- {
- colorLine(x-redrawTo.x, 0, x-redrawTo.x, avoid.size.height, vertLines[i].hue, 1);
- }
- }
-
- for (i=0; i<nhoriz; i++)
- {
- float y = horizLines[i].pos;
-
- if (y >= avoid.origin.y && y <= avoid.origin.y + avoid.size.height)
- {
- colorLine(0, y-redrawTo.y, avoid.size.width, y-redrawTo.y, horizLines[i].hue, 1);
- }
- }
-
- return self;
- }
-
- - inspector:sender
- {
- return [sender boinkInspector];
- }
-
-
-
- @end
-
-
-
-
-
-
-
-